﻿using eslib.CRClib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace eslib.nnp5
{
    /// <summary>
    /// 参数式数据包(基本数据包)
    /// 结构说明
    /// [command(2byte)]
    /// 
    /// --参数index索引从0开始
    /// --len不包括len长度本身
    /// {参数N-[参数长度len(1byte)][参数缓存(len byte)]}*N        
    /// </summary>
    public class ParamPackage : IPackage
    {
        /// <summary>
        /// 字节缓存
        /// </summary>
        public List<byte> buffer { get; protected set; }

        public int Len => buffer.Count;

        #region 数据包结构

        /// <summary>
        /// 获取指令
        /// </summary>
        /// <returns></returns>
        public ushort GetCommand()
        {
            if (Len < 2) throw new NNP5Exception("数据包长度不正确");
            return BitConverter.ToUInt16(buffer.ToArray(), 0);
        }


        /// <summary>
        /// 设置指令
        /// </summary>
        /// <param name="command"></param>
        public void SetCommand(ushort command)
        {
            if (Len < 2) buffer = new List<byte>() { 0, 0 };
            byte[] cmd = BitConverter.GetBytes(command);
            buffer[0] = cmd[0];
            buffer[1] = cmd[1];
        }

        #region 添加参数

        public void AddUtf8Param(string p)
        {
            AddParam(Encoding.UTF8.GetBytes(p));
        }

        public void AddAsciiParam(string p)
        {
            AddParam(Encoding.ASCII.GetBytes(p));
        }

        public void AddBoolParam(bool p)
        {
            AddParam(BitConverter.GetBytes(p));
        }

        public void AddFloatParam(float p)
        {
            AddParam(BitConverter.GetBytes(p));
        }

        public void AddDoubleParam(double p)
        {
            AddParam(BitConverter.GetBytes(p));
        }

        public void AddInt32Param(int p)
        {
            AddParam(BitConverter.GetBytes(p));
        }

        public void AddInt16Param(short p)
        {
            AddParam(BitConverter.GetBytes(p));
        }

        public void AddUInt32Param(UInt32 p)
        {
            AddParam(BitConverter.GetBytes(p));
        }

        public void AddUInt16Param(UInt16 p)
        {
            AddParam(BitConverter.GetBytes(p));
        }

        public void AddUInt8Param(byte p)
        {
            AddParam(new byte[] { p });
        }


        /// <summary>
        /// 添加struct参数
        /// T必需为struct，如:
        /// 
        /// //顺序式,1字节对齐，这里要和C++定义一致
        /// [StructLayout(LayoutKind.Sequential, Pack = 1)]
        /// public struct STest
        /// {
        ///    public int num;
        ///    //8字节数组
        ///    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        ///    public byte[] buf;
        /// }
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="s"></param>
        public void AddStructParam<T>(T s) where T : struct
        {
            byte[] buf = StructConvert.StructToBytes(s);
            AddParam(buf);
        }


        /// <summary>
        /// 添加参数,自动添加长度段
        /// </summary>
        /// <param name="p"></param>
        public void AddParam(byte[] p)
        {
            //添加长度
            buffer.Add((byte)p.Length);
            //添加缓存
            buffer.AddRange(p);
        }

        #endregion


        #region 获取参数


        public string GetUtf8Param(int index)
        {
            return Encoding.UTF8.GetString(GetParam(index));
        }

        public string GetAsciiParam(int index)
        {
            return Encoding.ASCII.GetString(GetParam(index));
        }

        public bool GetBoolParam(int index)
        {
            return BitConverter.ToBoolean(GetParam(index), 0);
        }

        public float GetFloatParam(int index)
        {
            return BitConverter.ToSingle(GetParam(index), 0);
        }


        public double GetDoubleParam(int index)
        {
            return BitConverter.ToDouble(GetParam(index), 0);
        }


        public int GetInt32Param(int index)
        {
            return BitConverter.ToInt32(GetParam(index), 0);
        }

        public short GetInt16Param(int index)
        {
            return BitConverter.ToInt16(GetParam(index), 0);
        }


        public UInt32 GetUInt32Param(int index)
        {
            return BitConverter.ToUInt32(GetParam(index), 0);
        }

        public UInt16 GetUInt16Param(int index)
        {
            return BitConverter.ToUInt16(GetParam(index), 0);
        }

        public byte GetUInt8Param(int index)
        {
            return GetParam(index)[0];
        }


        /// <summary>
        /// 获取struct参数,T必需C++类型struct,失败抛出异常
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="index"></param>
        /// <returns></returns>
        public T GetStructParam<T>(int index) where T : struct
        {
            byte[] buf = GetParam(index);
            return (T)StructConvert.BytesToStruct(buf, typeof(T));
        }


        /// <summary>
        /// 获取参数,失败抛出异常
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="index">index从0开始</param>
        /// <returns></returns>
        public byte[] GetParam(int index)
        {
            int ptr = 1;
            if (Len < (ptr + 1)) throw new NNP5Exception("数据包长度不正确");

            //跳过前参数
            for (int i = 0; i < index; i++)
            {
                //加前一参数的长度
                ptr += 1;
                int len = (int)buffer[ptr];
                ptr += len;     //加参数缓存长度
                if (Len < (ptr + 1)) throw new NNP5Exception("数据包长度不正确");
            }

            //获取当前参数
            ptr += 1;
            if (Len < (ptr + 1)) throw new NNP5Exception("数据包长度不正确");
            int plen = (int)buffer[ptr];

            if (Len < (ptr + plen + 1)) throw new NNP5Exception("数据包长度不正确");

            //跳过前段长度,取(plen)
            return buffer.Skip(ptr + 1).Take(plen).ToArray();
        }


        /// <summary>
        /// 获取参数数量
        /// </summary>
        /// <returns></returns>
        public int GetParamCount()
        {
            int count = 0;  //计数

            int ptr = 1;
            if (Len < 3) return count;

            while (true)
            {
                //加前一参数的长度
                ptr += 1;
                int len = (int)buffer[ptr];
                ptr += len;     //加参数缓存长度

                //增加计数
                count++;

                //缓存长度到尽头退出循环 
                if (Len <= (ptr + 1)) break;        //ptr+1即已计数的字节数
            }
            return count;
        }


        #endregion


        #endregion




        #region CRC计算相关

        /// <summary>
        /// 调取缓存,从索引0开始（即包含Command），获取paramCount个参数的连续缓存
        /// 比如要获取3个参数，输入3，将返回Command、参数0、参数1、参数2，共3个参数的连续缓存
        /// 返回要计算CRC的缓存长度
        /// </summary>
        /// <param name="paramCount">要获取的参数个数</param>
        /// <returns></returns>
        private int takeLen(int paramCount)
        {
            int ptr = 1;

            //只有Command，或者Command+长度0的参数0
            if (Len <= 3) return Len;       //全部计入

            for (int i = 0; i < paramCount; i++)
            {
                //加前一参数的长度
                ptr += 1;
                int len = (int)buffer[ptr];
                ptr += len;     //加参数缓存长度

                //缓存长度到尽头退出循环 
                if (Len <= (ptr + 1)) break;        //ptr+1即已计数的字节数
            }

            return ptr + 1;
        }


        /// <summary>
        /// 计算从索引0开始(即包含command)、到paramCount个参数的连续缓存的CRC值
        /// 比如要计入前3个参数，输出入，将计算Command、参数0、参数1、参数2，共3个参数的连续缓存的CRC值
        /// </summary>
        /// <param name="paramCount">要计入的参数个数</param>
        /// <returns></returns>
        public UInt32 ComputeCRC(int paramCount)
        {
            int calLen = takeLen(paramCount);

            EsCrc escrc = new EsCrc();
            return escrc.ComputeCrc(buffer.Take(calLen).ToArray());
        }


        #endregion



        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="command"></param>
        public ParamPackage(ushort command) : this()
        {
            buffer.AddRange(BitConverter.GetBytes(command));
        }

        /// <summary>
        /// 序列化构造
        /// </summary>
        public ParamPackage()
        {
            buffer = new List<byte>();
        }

        /// <summary>
        /// 以缓存构造
        /// </summary>
        /// <param name="pkgbuffer"></param>
        public ParamPackage(byte[] pkgbuffer) : this()
        {
            buffer.AddRange(pkgbuffer);
        }


        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <returns></returns>
        public byte[] GetBuffer()
        {
            return buffer.ToArray();
        }
    }
}
